#include "h_mulit_mempool.h"
#include "h_mempool.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define LEVEL_MIN_TOTAL     4
#define EXPAND_MIN_SIZE     8
#define CHUNK_MAX_SIZE      8 * 1024 *1024

h_mulit_mempool_t *h_mulit_mempool_new() {
    return (h_mulit_mempool_t *)calloc(1, sizeof(h_mulit_mempool_t));
}

bool h_mulit_mempool_init(h_mulit_mempool_t *m, uint32_t level, uint32_t total, uint32_t expand_num,
    uint32_t other_total, uint32_t chunk_size) {
    if (m == NULL || level == 0 || total == 0 || chunk_size == 0) {
        return false;
    }
    if (m->total > 0) {
        return false;
    }

    // 校验最大chunk的size，防止出现特大块
    uint32_t max_chunk_size = chunk_size;
    for (uint32_t i = 1; i < level; i++) {
        max_chunk_size *= 2;
    }
    if (max_chunk_size > CHUNK_MAX_SIZE) {
        return false;
    }
    
    m->mempools = (h_mempool_t *)calloc(level, sizeof(h_mempool_t));
    if (m->mempools == NULL) {
        return false;
    }
    if (!h_mempool_init(&m->mempools[0], total, expand_num, chunk_size)) {
        return false;
    }
    uint32_t level_total = total;
    uint32_t level_expand_num = expand_num;
    uint32_t level_chunk_size = chunk_size;
    for (uint32_t i = 1; i < level; i++) {
        if (other_total == 0) {
            level_total /= 2;
        }
        level_chunk_size *= 2;
        if (level_total < LEVEL_MIN_TOTAL) {
            level_total = LEVEL_MIN_TOTAL;
        }
        if (level_expand_num < EXPAND_MIN_SIZE) {
            level_expand_num = EXPAND_MIN_SIZE;
        }
        if (!h_mempool_init(&m->mempools[i], level_total, level_expand_num, level_chunk_size)) {
            return false;
        }
    }
    m->level = level;
    m->total = total;
    m->expand_num = expand_num;
    m->other_total = other_total;    
    m->chunk_size = chunk_size;
    return true;
}
    
void *h_mulit_mempool_get(h_mulit_mempool_t *m, uint32_t chunk_size) {
    if (m == NULL || chunk_size == 0 || m->mempools == NULL) {
        return NULL;
    }
    uint32_t i = 0;
    for (; i < m->level; i++ ) {
        if (m->mempools[i].chunk_size >= chunk_size) {
            break;
        }
    }
    if (i == m->level) {
        return NULL;
    }
    return h_mempool_get(&m->mempools[i]);
}

void h_mulit_mempool_put(h_mulit_mempool_t *m, void *data) {
    if (m == NULL || m->mempools == NULL || data == NULL) {
        return;
    }
    chunk_t * node = (chunk_t *)((uint8_t *)data - sizeof(chunk_t));
    uint32_t i = m->level - 1;
    for (; i >= 0; i-- ) {
        if (m->mempools[i].chunk_size <= node->size) {
            break;
        }
    }
    return h_mempool_put(&m->mempools[i], data);
}

void h_mulit_mempool_destroy(h_mulit_mempool_t *m) {
    if (m == NULL) {
        return;
    }
    if (m->mempools) {
        for (uint32_t i = 0; i < m->level; i++) {
            h_mempool_clear(&m->mempools[i]);
        }
        free(m->mempools);
    }
    free(m);
}

void h_mulit_mempool_print(h_mulit_mempool_t *m, bool detail) {
    if (m == NULL || m->mempools == NULL) {
        return;
    }
    printf("mulit mempool total :%u, expand_num :%u, other_total :%u, chunk_size :%u\n", m->total, m->expand_num, m->other_total, m->chunk_size);
    for (uint32_t i = 0; i < m->level; i++) {
        printf("mulit mempool level :%u\n", i);
        h_mempool_print(&m->mempools[i], detail);
    }
}